package com.laozj.test.aop;

import com.laozj.test.service.impl.UserServiceImpl;
import com.laozj.test.vo.UserVO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;

// 声明这是一个切面类
@Aspect
// 让Spring管理这个Bean
@Component
@Slf4j
public class DataAspect {


    // 定义切入点：匹配com.laozj.test.service.impl.UserServiceImpl包下save()和update()方法
    @Pointcut("execution(* com.laozj.test.service.impl.UserServiceImpl.save(..)) || " +
            "execution(* com.laozj.test.service.impl.UserServiceImpl.update(..))")
    public void saveOrUpdatePointcut() {}

    // 定义切入点：匹配com.laozj.test.service.impl包下所有类的所有方法
    @Pointcut("execution(* com.laozj.test.service.impl..*(..))")
    public void userServicePointcut() {}


    // 后置通知：方法执行后执行（无论是否异常）
    @After("userServicePointcut()")
    public void afterMethod(JoinPoint joinPoint) {
        // 日志处理
        String methodName = joinPoint.getSignature().getName();
        log.info("方法：{} 执行结束", methodName);
    }

    // 返回通知：方法正常返回后执行
    @AfterReturning(value = "saveOrUpdatePointcut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        // 数据处理

        int ans = 0;

        Class<?> aClass = joinPoint.getThis().getClass();
        joinPoint.getSignature().getDeclaringTypeName(); // com.laozj.test.service.impl.UserServiceImpl
        String typeName = joinPoint.getSignature().getDeclaringTypeName();


        if (result instanceof List) {
            List<?> resultList = (List<?>) result;
            LocalDateTime now = LocalDateTime.now();
            for (Object element : resultList) {
                if (element != null) {
                    try {
                        Method method = element.getClass().getMethod("update", LocalDateTime.class);
                        method.invoke(element, now);
                    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }

    // 异常通知：捕获方法执行异常并统计错误情况
//    @AfterThrowing(
//            pointcut = "userServicePointcut()",
//            throwing = "ex" // 绑定抛出的异常
//    )
    public void logMethodException(JoinPoint joinPoint, Exception ex) {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        LocalDateTime startTime = MethodTimeHolder.getStartTime();
        LocalDateTime errorTime = LocalDateTime.now();
        long duration = Duration.between(startTime, errorTime).toMillis();

        // 日志统计：包含异常信息、耗时、错误状态
        System.out.printf("[异常] %s.%s 执行失败，异常：%s，耗时：%dms，时间：%s%n",
                className, methodName, ex.getMessage(), duration, errorTime);

        // 清除ThreadLocal中的数据
        MethodTimeHolder.clear();
    }


    // 工具类：用ThreadLocal存储方法开始时间（解决多线程并发问题）
    private static class MethodTimeHolder {
        private static final ThreadLocal<LocalDateTime> startTimeHolder = new ThreadLocal<>();

        public static void setStartTime(LocalDateTime time) {
            startTimeHolder.set(time);
        }

        public static LocalDateTime getStartTime() {
            return startTimeHolder.get();
        }

        public static void clear() {
            startTimeHolder.remove();
        }
    }
}
